@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.
- package/dist/commonjs/features/chatbot/helpers.js +8 -1
- package/dist/commonjs/features/chatbot/helpers.js.map +1 -1
- package/dist/commonjs/features/chatbot/model/api.js +3 -7
- package/dist/commonjs/features/chatbot/model/api.js.map +1 -1
- package/dist/commonjs/features/chatbot/model/model.js +4 -1
- package/dist/commonjs/features/chatbot/model/model.js.map +1 -1
- package/dist/commonjs/features/chatbot/model/t2s.js +10 -15
- package/dist/commonjs/features/chatbot/model/t2s.js.map +1 -1
- package/dist/commonjs/features/chatbot/types/model.types.js.map +1 -1
- package/dist/commonjs/features/feedback/types.js.map +1 -1
- package/dist/module/features/chatbot/helpers.js +5 -0
- package/dist/module/features/chatbot/helpers.js.map +1 -1
- package/dist/module/features/chatbot/model/api.js +3 -7
- package/dist/module/features/chatbot/model/api.js.map +1 -1
- package/dist/module/features/chatbot/model/model.js +5 -2
- package/dist/module/features/chatbot/model/model.js.map +1 -1
- package/dist/module/features/chatbot/model/t2s.js +8 -13
- package/dist/module/features/chatbot/model/t2s.js.map +1 -1
- package/dist/module/features/chatbot/types/model.types.js.map +1 -1
- package/dist/module/features/feedback/types.js.map +1 -1
- package/dist/typescript/commonjs/features/chatbot/helpers.d.ts +1 -0
- package/dist/typescript/commonjs/features/chatbot/helpers.d.ts.map +1 -1
- package/dist/typescript/commonjs/features/chatbot/model/api.d.ts +2 -3
- package/dist/typescript/commonjs/features/chatbot/model/api.d.ts.map +1 -1
- package/dist/typescript/commonjs/features/chatbot/model/model.d.ts +3 -3
- package/dist/typescript/commonjs/features/chatbot/model/model.d.ts.map +1 -1
- package/dist/typescript/commonjs/features/chatbot/model/t2s.d.ts +10 -4
- package/dist/typescript/commonjs/features/chatbot/model/t2s.d.ts.map +1 -1
- package/dist/typescript/commonjs/features/chatbot/types/api.types.d.ts +4 -0
- package/dist/typescript/commonjs/features/chatbot/types/api.types.d.ts.map +1 -1
- package/dist/typescript/commonjs/features/chatbot/types/model.types.d.ts +2 -2
- package/dist/typescript/commonjs/features/chatbot/types/model.types.d.ts.map +1 -1
- package/dist/typescript/commonjs/features/feedback/types.d.ts +1 -0
- package/dist/typescript/commonjs/features/feedback/types.d.ts.map +1 -1
- package/dist/typescript/module/features/chatbot/helpers.d.ts +1 -0
- package/dist/typescript/module/features/chatbot/helpers.d.ts.map +1 -1
- package/dist/typescript/module/features/chatbot/model/api.d.ts +2 -3
- package/dist/typescript/module/features/chatbot/model/api.d.ts.map +1 -1
- package/dist/typescript/module/features/chatbot/model/model.d.ts +3 -3
- package/dist/typescript/module/features/chatbot/model/model.d.ts.map +1 -1
- package/dist/typescript/module/features/chatbot/model/t2s.d.ts +10 -4
- package/dist/typescript/module/features/chatbot/model/t2s.d.ts.map +1 -1
- package/dist/typescript/module/features/chatbot/types/api.types.d.ts +4 -0
- package/dist/typescript/module/features/chatbot/types/api.types.d.ts.map +1 -1
- package/dist/typescript/module/features/chatbot/types/model.types.d.ts +2 -2
- package/dist/typescript/module/features/chatbot/types/model.types.d.ts.map +1 -1
- package/dist/typescript/module/features/feedback/types.d.ts +1 -0
- package/dist/typescript/module/features/feedback/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/features/chatbot/helpers.ts +16 -1
- package/src/features/chatbot/model/api.ts +5 -17
- package/src/features/chatbot/model/model.ts +243 -252
- package/src/features/chatbot/model/t2s.ts +90 -92
- package/src/features/chatbot/types/api.types.ts +5 -0
- package/src/features/chatbot/types/model.types.ts +44 -43
- package/src/features/feedback/types.ts +1 -0
|
@@ -1,107 +1,105 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { restore, createEvent } from 'effector'
|
|
1
|
+
import { restore, createEvent, Store } from 'effector'
|
|
3
2
|
import { AUDIO_CONFIG, DEFAULT_VOICE } from '../constants'
|
|
4
3
|
import { AudioProvider, AudioStatus, PlayTextToSpeechProps } from '../types/t2s.types'
|
|
4
|
+
import { ChatbotAPI } from './api'
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
type ChatTextToSpeechModelProps = {
|
|
7
|
+
$isOpen: Store<boolean>
|
|
8
|
+
api: ChatbotAPI
|
|
9
|
+
}
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
export class ChatTextToSpeechModel {
|
|
12
|
+
private readonly api: ChatbotAPI
|
|
13
|
+
private readonly audioCache: Record<string, string> = {}
|
|
14
|
+
private audioProvider: AudioProvider | null = null
|
|
15
|
+
|
|
16
|
+
readonly setCurrentPlayingId = createEvent<string | null>()
|
|
17
|
+
readonly $currentPlayingId = restore(this.setCurrentPlayingId, null)
|
|
18
|
+
readonly setStatus = createEvent<AudioStatus>()
|
|
19
|
+
readonly $status = restore(this.setStatus, AudioStatus.STOPPED)
|
|
20
|
+
|
|
21
|
+
constructor({ $isOpen, api }: ChatTextToSpeechModelProps) {
|
|
22
|
+
this.api = api
|
|
23
|
+
|
|
24
|
+
$isOpen.updates.watch(isOpen => {
|
|
25
|
+
if (!isOpen) this.reset()
|
|
26
|
+
})
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
public setAudioProvider(audioProvider: AudioProvider) {
|
|
30
|
+
this.audioProvider = audioProvider
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
private getCacheKey = (messageId: string, isTranslated: boolean) => {
|
|
34
|
+
return `${messageId}-${isTranslated ? 'translated' : 'original'}`
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async play({ text, messageId, isTranslated, config }: PlayTextToSpeechProps) {
|
|
38
|
+
if (!this.audioProvider) throw new Error('Audio provider is not set. Use setAudioProvider.')
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
const status = this.$status.getState()
|
|
42
|
+
const playingId = this.$currentPlayingId.getState()
|
|
43
|
+
const isPlaying = status === AudioStatus.PLAYING
|
|
44
|
+
const isPaused = status === AudioStatus.PAUSED
|
|
45
|
+
const isCurrentMessage = messageId === playingId
|
|
46
|
+
|
|
47
|
+
if (isPlaying && isCurrentMessage) {
|
|
48
|
+
this.audioProvider.pause()
|
|
49
|
+
this.setStatus(AudioStatus.PAUSED)
|
|
50
|
+
return
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (isPaused && isCurrentMessage) {
|
|
54
|
+
this.audioProvider.resume()
|
|
55
|
+
this.setStatus(AudioStatus.PLAYING)
|
|
56
|
+
return
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (messageId && !isCurrentMessage) {
|
|
60
|
+
this.audioProvider.stop()
|
|
61
|
+
}
|
|
15
62
|
|
|
16
|
-
|
|
17
|
-
|
|
63
|
+
const cacheKey = this.getCacheKey(messageId, isTranslated)
|
|
64
|
+
let audioPath = this.audioCache[cacheKey]
|
|
65
|
+
this.setCurrentPlayingId(messageId)
|
|
18
66
|
|
|
19
|
-
|
|
20
|
-
|
|
67
|
+
if (!audioPath) {
|
|
68
|
+
const response = await this.api.textToSpeechTextFx({
|
|
69
|
+
input: { text },
|
|
70
|
+
voice: config?.voice ?? DEFAULT_VOICE,
|
|
71
|
+
audioConfig: config?.audioConfig ?? AUDIO_CONFIG,
|
|
21
72
|
})
|
|
22
|
-
}
|
|
23
73
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
return `${messageId}-${isTranslated ? 'translated' : 'original'}`
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
async play({ text, messageId, isTranslated, config }: PlayTextToSpeechProps) {
|
|
33
|
-
if (!this.audioProvider) throw new Error('Audio provider is not set. Use setAudioProvider.')
|
|
34
|
-
|
|
35
|
-
try {
|
|
36
|
-
const status = this.$status.getState()
|
|
37
|
-
const playingId = this.$currentPlayingId.getState()
|
|
38
|
-
const isPlaying = status === AudioStatus.PLAYING
|
|
39
|
-
const isPaused = status === AudioStatus.PAUSED
|
|
40
|
-
const isCurrentMessage = messageId === playingId
|
|
41
|
-
|
|
42
|
-
if (isPlaying && isCurrentMessage) {
|
|
43
|
-
this.audioProvider.pause()
|
|
44
|
-
this.setStatus(AudioStatus.PAUSED)
|
|
45
|
-
return
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (isPaused && isCurrentMessage) {
|
|
49
|
-
this.audioProvider.resume()
|
|
50
|
-
this.setStatus(AudioStatus.PLAYING)
|
|
51
|
-
return
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (messageId && !isCurrentMessage) {
|
|
55
|
-
this.audioProvider.stop()
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const cacheKey = this.getCacheKey(messageId, isTranslated)
|
|
59
|
-
let audioPath = this.audioCache[cacheKey]
|
|
60
|
-
this.setCurrentPlayingId(messageId)
|
|
61
|
-
|
|
62
|
-
if (!audioPath) {
|
|
63
|
-
const response = await this.model.api.textToSpeechTextFx({
|
|
64
|
-
input: { text },
|
|
65
|
-
voice: config?.voice ?? DEFAULT_VOICE,
|
|
66
|
-
audioConfig: config?.audioConfig ?? AUDIO_CONFIG,
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
// Response might be different type based on provided AUDIO_CONFIG
|
|
70
|
-
if (typeof response === 'string') {
|
|
71
|
-
audioPath = `data:audio/mpeg;base64,${response}`
|
|
72
|
-
} else {
|
|
73
|
-
audioPath = response?.audioContent ?? ''
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
this.audioCache[cacheKey] = audioPath
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
this.setStatus(AudioStatus.PLAYING)
|
|
80
|
-
await this.audioProvider.play(audioPath, () => {
|
|
81
|
-
this.setStatus(AudioStatus.STOPPED)
|
|
82
|
-
this.setCurrentPlayingId(null)
|
|
83
|
-
})
|
|
84
|
-
} catch (error) {
|
|
85
|
-
this.reset()
|
|
86
|
-
console.error('Error playing text-to-speech: ', error)
|
|
74
|
+
// Response might be different type based on provided AUDIO_CONFIG
|
|
75
|
+
if (typeof response === 'string') {
|
|
76
|
+
audioPath = `data:audio/mpeg;base64,${response}`
|
|
77
|
+
} else {
|
|
78
|
+
audioPath = response?.audioContent ?? ''
|
|
87
79
|
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
public stop() {
|
|
91
|
-
if (!this.audioProvider) throw new Error('Audio provider is not set. Use setAudioProvider.')
|
|
92
|
-
this.audioProvider.stop()
|
|
93
|
-
}
|
|
94
80
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
// if (!isOpen) {
|
|
98
|
-
// chatbotModel.t2s.stop();
|
|
99
|
-
// chatbotModel.t2s.reset();
|
|
100
|
-
// }
|
|
81
|
+
this.audioCache[cacheKey] = audioPath
|
|
82
|
+
}
|
|
101
83
|
|
|
102
|
-
|
|
103
|
-
|
|
84
|
+
this.setStatus(AudioStatus.PLAYING)
|
|
85
|
+
await this.audioProvider.play(audioPath, () => {
|
|
104
86
|
this.setStatus(AudioStatus.STOPPED)
|
|
105
87
|
this.setCurrentPlayingId(null)
|
|
88
|
+
})
|
|
89
|
+
} catch (error) {
|
|
90
|
+
this.reset()
|
|
91
|
+
console.error('Error playing text-to-speech: ', error)
|
|
106
92
|
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
public stop() {
|
|
96
|
+
if (!this.audioProvider) throw new Error('Audio provider is not set. Use setAudioProvider.')
|
|
97
|
+
this.audioProvider.stop()
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
public reset() {
|
|
101
|
+
this.stop()
|
|
102
|
+
this.setStatus(AudioStatus.STOPPED)
|
|
103
|
+
this.setCurrentPlayingId(null)
|
|
104
|
+
}
|
|
107
105
|
}
|
|
@@ -1,27 +1,28 @@
|
|
|
1
1
|
import type { Store } from 'effector'
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
3
|
+
GetAlternativesResponse,
|
|
4
|
+
PostMessagePayload,
|
|
5
|
+
PostMessageResponse,
|
|
6
|
+
GoogleTranslatePayload,
|
|
7
|
+
GoogleTranslateResponse,
|
|
8
|
+
GoogleText2SpeechPayload,
|
|
9
|
+
GoogleText2SpeechResponse,
|
|
10
|
+
ChatAnswer,
|
|
11
|
+
GetAlternativesPayload,
|
|
11
12
|
} from './api.types'
|
|
12
13
|
|
|
13
14
|
export type ConversationMessage = {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
id: string
|
|
16
|
+
message: string
|
|
17
|
+
threadId: string
|
|
18
|
+
options: ChatAnswer[]
|
|
19
|
+
isOwnMessage: boolean
|
|
20
|
+
translatedMessage?: string
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
export type EntityIds = {
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
entityId: string
|
|
25
|
+
fieldId: string
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
export type ChatMessagesModel = EntityIds & { messages: ConversationMessage[] }
|
|
@@ -29,45 +30,45 @@ export type ThreadIdsModel = EntityIds & { threadId: string }
|
|
|
29
30
|
export type AvailableChatsModel = EntityIds & { isAvailable: boolean }
|
|
30
31
|
|
|
31
32
|
export type ChatbotModelProps = {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
33
|
+
credits: {
|
|
34
|
+
$assignmentId: Store<string>
|
|
35
|
+
$exerciseId: Store<string>
|
|
36
|
+
}
|
|
37
|
+
api: {
|
|
38
|
+
getAlternatives: (props: GetAlternativesPayload) => Promise<GetAlternativesResponse>
|
|
39
|
+
postMessage: (props: PostMessagePayload) => Promise<PostMessageResponse>
|
|
40
|
+
postTranslateText: (props: GoogleTranslatePayload) => Promise<GoogleTranslateResponse>
|
|
41
|
+
postTextToSpeech: (props: GoogleText2SpeechPayload) => Promise<GoogleText2SpeechResponse>
|
|
42
|
+
}
|
|
43
|
+
messages: {
|
|
44
|
+
error: ConversationMessage
|
|
45
|
+
start: ConversationMessage
|
|
46
|
+
}
|
|
46
47
|
}
|
|
47
48
|
|
|
48
49
|
export type SendHelpParams = {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
50
|
+
payload: PostMessagePayload
|
|
51
|
+
message: string
|
|
52
|
+
translationTargetLanguage: string
|
|
53
|
+
translatedMessage?: string
|
|
53
54
|
}
|
|
54
55
|
|
|
55
56
|
export type SendHelpProps = SendHelpParams & {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
assignmentId: string
|
|
58
|
+
exerciseId: string
|
|
59
|
+
messages: Record<string, Record<string, { messages: ConversationMessage[] }>>
|
|
60
|
+
isTranslated: boolean
|
|
60
61
|
}
|
|
61
62
|
|
|
62
63
|
export type SetHintMessageProps = EntityIds & ConversationMessage
|
|
63
64
|
|
|
64
65
|
export enum MessageVariant {
|
|
65
|
-
|
|
66
|
-
|
|
66
|
+
SENT = 'sent',
|
|
67
|
+
RECEIVED = 'received',
|
|
67
68
|
}
|
|
68
69
|
|
|
69
70
|
export type ChatbotRenderer = {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
71
|
+
message: string
|
|
72
|
+
variant: MessageVariant
|
|
73
|
+
withTextToSpeech: boolean
|
|
73
74
|
}
|